<?php
/**
 * @version			$Id$
 * @create 			2014年08月04日 星期一 12时34分25秒
 * @author 			ytf606 <ytf606@gmail.com,http://weibo.com/ytf606>
 * @package 		None
 * @subpackage 		None
 * @copyRight 		
 * 
 */

if (!function_exists('db')) {

    /**
     * init db 
     * 
     * @desc
     * 
     * @access public
     * @param $source=false
     * @return void
     * @exception none
     */
    function db($source=false, $db_config=null)
    {
        $dbhdlk = "LZ_DB";
        $dbuk = "username";
        $dbpk = "password";
        $dbhk = "host";
        $dbnk = "db_name";
        $dbck = "db_charset";
        $dbptk = "db_port";
        if ($source) {
            $dbuk .= "_" . $source;
            $dbpk .= "_" . $source;
            $dbhk .= "_" . $source;
            $dbnk .= "_" . $source;
            $dbptk .= "_" . $source;
            $dbhdlk .= "_" . $source;
        }
        $GLOBALS['LZ_DB_KEY'] = $dbhdlk;

        if (isset($GLOBALS[$dbhdlk]) && $GLOBALS[$dbhdlk]->ping()) {
            return $GLOBALS[$dbhdlk];
        }
        if (!$db_config) {
            include "db.config.php";
            $db_config = $config;
        }
        $GLOBALS[$dbhdlk] = new mysqli ($db_config[$dbhk], $db_config[$dbuk], $db_config[$dbpk], $db_config[$dbnk], $db_config[$dbptk]);
        if ($GLOBALS[$dbhdlk]->connect_errno) {
            sleep(1);
            $GLOBALS[$dbhdlk] = new mysqli ($db_config[$dbhk], $db_config[$dbuk], $db_config[$dbpk], $db_config[$dbnk], $db_config[$dbptk]);
            if ($GLOBALS[$dbhdlk]->connect_errno) {
                _set_error($GLOBALS[$dbhdlk]->connect_errno, $GLOBALS[$dbhdlk]->connect_error);
                return false;
            }
        }
        set_charset($db_config[$dbck], $source);
        return $GLOBALS[$dbhdlk];
    }
}

if (!function_exists('get_data')) {

    /**
     * get fetch all data 
     * 
     * @desc
     * 
     * @access public
     * @param $sql
     * @param $data=array(
     * @return void
     * @exception none
     */
    function get_data($sql, $data=array(), $source=false)
    {
        $stmt = _stmt_query($sql, $data, $source);  
        if (false === $stmt) {
            return false;
        }
        $parameters = array();
        $results = array();
        $meta = $stmt->result_metadata();
        if(!$meta && $stmt->sqlstate) { 
            return array();
        }
        $row = array();
        while ($field = $meta->fetch_field()) {
            $row[$field->name] = null;
            $parameters[] = &$row[$field->name];
        }
        
        // avoid out of memory bug in php 5.2 and 5.3
        version_compare(phpversion(), '5.4', '<') && $stmt->store_result();
        call_user_func_array(array($stmt, 'bind_result'), $parameters);
        while ($ret = $stmt->fetch()) {
            $x = array();
            foreach ($row as $key => $val) {
                $x[$key] = $val;
            }
            array_push($results, $x);
        }
        $stmt->free_result();
        return $results;
    }
}

if ( !function_exists('get_line') ) {

    /**
     * get a line data from sql query 
     * 
     * @desc
     * 
     * @access public
     * @param $sql
     * @param $data=array(
     * @return void
     * @exception none
     */
    function get_line($sql, $data=array(), $source=false)
    {
        $data = get_data($sql, $data, $source);
        if (!$data) {
            return false;
        }
        return reset($data);
    }
}

if ( !function_exists('get_var') ) {

    /**
     * get a var data from sql query 
     * 
     * @desc
     * 
     * @access public
     * @param $sql
     * @param $data=array(
     * @return void
     * @exception none
     */
    function get_var($sql, $data=array(), $source=false)
    {
        $data = get_line($sql, $data, $source);
        if (!$data) {
            return false;
        }
        return $data[reset(array_keys($data))];
    }
}

if ( !function_exists('get_var_array') ) {

    /**
     * get a var array data from sql query
     * 
     * @desc
     * 
     * @access public
     * @param $sql
     * @param $data=array(
     * @return void
     * @exception none
     */
    function get_var_array($sql, $data=array(), $source=false)
    {
        $data = get_data($sql, $data, $source);
        if (!$data) {
            return false;
        }
        return array_map(function($v){return reset($v);}, $data);
    }
}

if ( !function_exists('run_sql') ) {

    /**
     * execute insert|update|delete|replace... sql 
     * 
     * @desc
     * 
     * @access public
     * @param $sql
     * @param $data=array(
     * @return void
     * @exception none
     */
    function run_sql($sql, $data=array(), $source=false)
    {
        $stmt = _stmt_query($sql, $data, $source);
        return ($stmt && $stmt->affected_rows > 0 ? $stmt->insert_id : false);
    }
}
if ( !function_exists('get_errno') ) {

    /**
     * get last sql executed errno 
     * 
     * @desc
     * 
     * @access public
     * @return void
     * @exception none
     */
    function get_errno()
    {
        $errno_key = $GLOBALS['LZ_DB_KEY'] . "_ERRNO";
        return $GLOBALS[$errno_key]; 
    }
}

if ( !function_exists('get_error') ) {

    /**
     * get last sql executed error 
     * 
     * @desc
     * 
     * @access public
     * @return void
     * @exception none
     */
    function get_error()
    {
        $error_key = $GLOBALS['LZ_DB_KEY'] . "_ERROR";
        return $GLOBALS[$error_key]; 
    }
}

if ( !function_exists('last_id') ) {

    /**
     * get insert or replace last id 
     * 
     * @desc
     * 
     * @access public
     * @param $source=false
     * @return void
     * @exception none
     */
    function last_id($source=false)
    {
        return isset($GLOBALS['STMT']) ? $GLOBALS['STMT']->insert_id : db($source)->insert_id;
    }
}

if ( !function_exists('count_rows') ) {

    /**
     * get the number of rows in statments result set 
     * 
     * @desc
     * 
     * @access public
     * @return void
     * @exception none
     */
    function count_rows()
    {
        return isset($GLOBALS['STMT']) ? $GLOBALS['STMT']->num_rows : false; 
    }
}

if ( !function_exists('affected_rows') ) {

    /**
     * get affected rows from last sql executed 
     * 
     * @desc
     * 
     * @access public
     * @param $source=false
     * @return void
     * @exception none
     */
    function affected_rows($source=false)
    {
        return isset($GLOBALS['STMT']) ? $GLOBALS['STMT']->affected_rows : db($source)->affected_rows;
    }
}

if ( !function_exists('escape') ) {

    /**
     * excapes special characters in a string for use in an SQL statement 
     * 
     * @desc
     * 
     * @access public
     * @param $str
     * @param $source=false
     * @return void
     * @exception none
     */
    function escape($str, $source=false)
    {
        return db($source)->real_escape_string($str);
    }
}

if ( !function_exists('ping') ) {

    /**
     * pings the server connection  
     * 
     * @desc
     * 
     * @access public
     * @param $source=false
     * @return void
     * @exception none
     */
    function ping($source=false)
    {
        return db($source)->ping();
    }
}

if ( !function_exists('begin_trans') ) {

    /**
     * starts a transaction 
     * 
     * @desc
     * 
     * @access public
     * @param $source=false
     * @return void
     * @exception none
     */
    function begin_trans($source=false)
    {
        return db($source)->autocommit(true);
    }
}

if ( !function_exists('commit_trans') ) {

    /**
     * commit a transaction 
     * 
     * @desc
     * 
     * @access public
     * @param $source=false
     * @return void
     * @exception none
     */
    function commit_trans($source=false)
    {
        return db($source)->commit();
    }
}

if ( !function_exists('rollback_trans') ) {

    /**
     * rollback the current transaction 
     * 
     * @desc
     * 
     * @access public
     * @param $source=false
     * @return void
     * @exception none
     */
    function rollback_trans($source=false)
    {
        return db($source)->rollback();
    }
}

if ( !function_exists('select_db') ) {

    /**
     * select a db  
     * 
     * @desc
     * 
     * @access public
     * @param $dbname
     * @param $source=false
     * @return void
     * @exception none
     */
    function select_db($dbname, $source=false){
        return db($source)->select_db($dbname);
    }
}

if ( !function_exists('set_charset') ) {

    /**
     * set the current db charset 
     * 
     * @desc
     * 
     * @access public
     * @param $charset
     * @param $source=false
     * @return void
     * @exception none
     */
    function set_charset($charset, $source=false)
    {
        return db($source)->set_charset($charset);
    }
}

if ( !function_exists('close_db')) {

    /**
     * close mysql link 
     * 
     * @desc
     * 
     * @access public
     * @param $source=false
     * @return void
     * @exception none
     */
    function close_db($source=false)
    {
        unset($GLOBALS['LZ_DB_KEY']);
        return db($source)->close();
    }
}

/**
 * mysqli stmt query  
 * 
 * @desc
 * 
 * @access private
 * @param $sql
 * @param $data=array(
 * @param $source=false
 * @return void
 * @exception none
 */
function _stmt_query($sql, $data=array(), $source=false)
{
    _set_error();
    $type_str = _check_params($sql, $data);
    if (false === $type_str) {
        _set_error(20001, 'params error');
        return false;
    }
    $sql = _build_sql($sql);
    $stmt = _prepare_sql($sql, $source);
    if (false === $stmt) {
        return false;
    }
    if ($data && is_array($data)) {
        call_user_func_array(array($stmt, 'bind_param'), _ref_values(array_merge(array($type_str), $data))); 
        0!==$stmt->errno &&  _set_error($stmt->errno, $stmt->error);
    }
    $stmt->execute();
    0!==$stmt->errno && _set_error($stmt->errno, $stmt->error);
    $GLOBALS['STMT'] = $stmt;
    return $stmt;
}


/**
 * mysqli prepare sql query  
 * 
 * @desc
 * 
 * @access private
 * @param $sql
 * @param $source=false
 * @return void
 * @exception none
 */
function _prepare_sql($sql, $source=false)
{
    $db = db($source);
    $sql_md5 = md5($sql);
    $smd = $GLOBALS['LZ_DB_KEY'] . "_SQL";
    $dbs = $GLOBALS['LZ_DB_KEY'] . "_STMT";
    if (!isset($GLOBALS[$smd]) || $GLOBALS[$smd] !== $sql_md5) {
        isset($GLOBALS['STMT']) && $GLOBALS['STMT']->close();
        $GLOBALS[$smd] = $sql_md5;
        $GLOBALS[$dbs] = $db->prepare($sql);
        0!==$db->errno && _set_error($db->errno, $db->error);
    }
    return $GLOBALS[$dbs];
}


/**
 * replace source sql to prepare sql 
 * 
 * @desc
 * 
 * @access private
 * @param $sql
 * @return void
 * @exception none
 */
function _build_sql($sql)
{
    return preg_replace('/\s?(\?)(\w+)\s?/', '\\1\\3', $sql);
}


/**
 * check params from sql data 
 * 
 * @desc
 * 
 * @access private
 * @param $sql
 * @param $data
 * @return void
 * @exception none
 */
function _check_params($sql, $data)
{
    if (false === strpos($sql, "?")) {
        return true;
    }
    preg_match_all('/\s?\?([a-z])?(\d+)?\s?/', $sql, $matches);
    $type_key = array('i', 'd', 's', 'b');
    $type_value = array('is_int', 'is_float', 'is_string', 'is_bool');
    extract(array_combine($type_key, $type_value));
    $type_str = '';
    foreach ($matches[1] as $key=>$type) {
        if (!empty($matches[2][$key]) && strlen($data[$key]) > $matches[2][$key]) { 
            return false;
        }
        if (!in_array($type, $type_key)) {
            return false; 
        }
        true === $$type($data[$key]) && $type_str .= $type;
    }
    return strlen($type_str) === count($data) ? $type_str : false;
}


/**
 * bind params for sql prepare 
 * 
 * @desc
 * 
 * @access private
 * @param $arr
 * @return void
 * @exception none
 */
function _ref_values($arr)
{
    if (strnatcmp(phpversion(),'5.3') >= 0) {//Reference is required for PHP 5.3+ 
        $refs = array();
        foreach($arr as $key => $value) 
            $refs[$key] = &$arr[$key];
        return $refs; 
    }
    return $arr; 
}


/**
 * set or reset error 
 * 
 * @desc
 * 
 * @access private
 * @param $errno=0
 * @param $error='success'
 * @return void
 * @exception none
 */
function _set_error($errno=0, $error='success')
{
    isset($GLOBALS['LZ_DB_KEY']) || $GLOBALS['LZ_DB_KEY'] = "LZ_DB";
    $errno_key = $GLOBALS['LZ_DB_KEY'] . "_ERRNO";
    $error_key = $GLOBALS['LZ_DB_KEY'] . "_ERROR";
    $GLOBALS[$errno_key] = $errno; 
    $GLOBALS[$error_key] = $error; 
}
?>
